﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Kugar.Core.ExtMethod;
using Kugar.Core.Web;
using Kugar.UI.Web.AdminLTE.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Kugar.UI.Web.AdminLTE.Filters
{
    /// <summary>
    /// 用于兼容旧版,新版请使用 [AdminAuthorizeScheme("不同后台名称")]
    /// </summary>
    [Obsolete]
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public class AdminAuthorityAttribute:AuthorizeAttribute
    {
        private static byte[] _jwtKeyHWM256 = null;
        private static string _desKey = "";
        private static string _jwtKey = "1981217";

        static AdminAuthorityAttribute()
        {
            _jwtKeyHWM256 = Encoding.UTF8.GetBytes(_jwtKey.PadRight(48, ' '));
            _desKey = _jwtKey.Left(8).PadLeft(8, ' ');
        }

        /// <summary>
        /// 定义使用管理员授权校验
        /// </summary>
        /// <param name="loginUrl">登录地址,默认为 /AdminCore/AdminLogin/Login </param>
        /// <param name="jwtKey">jwt加密秘钥 默认为 19861217</param>
        /// <param name="cookieOrHeaderName">写入cookie或者header作为token的key的名称</param>
        public AdminAuthorityAttribute(string loginUrl="") : base("AdminLteDefaultAdminCheck")
        {
            if (!string.IsNullOrWhiteSpace(loginUrl))
            {
                LoginUrl = loginUrl;
            }
            
        }

        public static string CookieOrHeaderName { set; get; } = "aukss";

        public string LoginUrl { set; get; } = "/AdminCore/AdminLogin/Login";

        public static string JWTKey
        {
            set
            {
                _jwtKey = value;
                _jwtKeyHWM256 = Encoding.UTF8.GetBytes(_jwtKey.PadRight(48, ' '));
                _desKey = _jwtKey.Left(8).PadLeft(8, ' ');
            }
            get => _jwtKey;
        }

        internal static byte[] JWTKeyHWM256 => _jwtKeyHWM256;
        internal static string DesKey => _desKey;
    }

    public class AdminAuthorityRequirement : IAuthorizationRequirement
    {
        //Add any custom requirement properties if you have them
        public AdminAuthorityRequirement()
        {

        }
    }

    //public abstract class AttributeAuthorizationHandler<TRequirement, TAttribute> : AuthorizationHandler<TRequirement> where TRequirement : IAuthorizationRequirement where TAttribute : Attribute
    //{
    //    private static ConcurrentDictionary<MemberInfo, IEnumerable<Attribute>> _attributes=new ConcurrentDictionary<MemberInfo, IEnumerable<Attribute>>();

    //    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement)
    //    {
    //        IEnumerable<Attribute> attributes =null;

    //        var action = (context.Resource as AuthorizationFilterContext)?.ActionDescriptor as ControllerActionDescriptor;

    //        if (action != null)
    //        {
    //            attributes = GetAttributes(action.MethodInfo);
    //        }

    //        return HandleRequirementAsync(context, requirement, attributes);
    //    }

    //    protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable<Attribute> attributes);

    //    private IEnumerable<Attribute> GetAttributes(MemberInfo memberInfo)
    //    {
    //        return _attributes.GetOrAdd(memberInfo, getActionAttribute);
    //    }

    //    private IEnumerable<Attribute> getActionAttribute(MemberInfo member)
    //    {
    //        var attributes = new List<Attribute>();
    //        //member.DeclaringType
    //        attributes.AddRange(member.DeclaringType.GetCustomAttributes<TAttribute>(true));
    //        attributes.AddRange(member.GetCustomAttributes<TAttribute>(true));

    //        return attributes;
    //    }
    //}

    public class AdminAuthorityPolicyHandler : AttributeAuthorizationHandler<AdminAuthorityRequirement, AdminAuthorityAttribute>
    {
        private IAdminLoginManager _manager = null;

        public AdminAuthorityPolicyHandler(IAdminLoginManager manager)
        {
            _manager = manager;

            if (manager==null)
            {
                throw new Exception("IAdminManager接口未注入实现,请实现该接口并注入");
            }
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizationFilterContext filterContext, AdminAuthorityRequirement requirement, IEnumerable<AdminAuthorityAttribute> attributes)
        {
            var attr =(AdminAuthorityAttribute) attributes.FirstOrDefault();

            var context2 = context.Resource as AuthorizationFilterContext;

            var token = "";

            if (context2.HttpContext.Request.Cookies.TryGetValue(AdminAuthorityAttribute.CookieOrHeaderName,out var v))
            {
                token = v;
            }

            if (string.IsNullOrEmpty(token))
            {
                if (context2.HttpContext.Request.Headers.TryGetValue(AdminAuthorityAttribute.CookieOrHeaderName, out var v2))
                {
                    token = v2;
                }
            }

            string userID = "";

            if (!string.IsNullOrEmpty(token))
            {
                try
                {
                    var userInfo=readToken(token, AdminAuthorityAttribute.JWTKeyHWM256, AdminAuthorityAttribute.DesKey);

                    var ret=_manager.Login(userInfo.userName, userInfo.password);

                    if (ret.IsSuccess)
                    {
                        var indent = new AdminLteUser();

                        userID = ret.ReturnData;

                        indent.AddClaim(new Claim("UserName", userInfo.userName));
                        indent.AddClaim(new Claim("UserID",ret.ReturnData));
                        indent.AddClaim(new Claim("LoginUrl",attr.LoginUrl));
                        context.User.AddIdentity(indent);

                        context.Succeed(requirement);

                        return Task.CompletedTask;
                    }
                }
                catch (Exception e)
                {
                    
                }
            }
            
            if(string.IsNullOrWhiteSpace(userID))
            {
                var action = (context.Resource as AuthorizationFilterContext)?.ActionDescriptor as ControllerActionDescriptor;

                if (!action.MethodInfo.GetCustomAttributes(typeof(AllowAnonymousAttribute)).HasData())
                {
                    context2.Result = new RedirectResult($"{attr.LoginUrl}?backurl={HttpUtility.UrlEncode(context2.HttpContext.Request.GetDisplayUrl())}");

                }
                
                context.Succeed(requirement);
            }

            //context2.Result=new RedirectResult(attr.LoginUrl);
            //context.Succeed(requirement);

            return Task.CompletedTask;
        }

        private (string userName, string password) readToken(string token, byte[] jwtKey,string desKey)
        {
            //jwtKey = jwtKey.PadRight(48, ' ');

            var securityKey = new SymmetricSecurityKey(jwtKey);


            var s = (new JwtSecurityTokenHandler()).ValidateToken(token, new TokenValidationParameters()
                                                                         {
                                                                             ValidateLifetime = false,
                                                                             ValidateIssuer = false,
                                                                             ValidateActor = false,
                                                                             ValidateAudience = false,
                                                                             RequireExpirationTime = false,

                                                                             //ValidateIssuerSigningKey = true,
                                                                             IssuerSigningKey = securityKey
                                                                         }, out var s1);

            var item = s.Identities.FirstOrDefault();

            var claim = item.Claims.FirstOrDefault();

            var json = JObject.Parse(claim.Value.DesDecrypt(desKey));

            //var value = JObject.Parse(json.GetString("key").DesDecrypt(AdminAuthorityAttribute.DesKey));

            return (json.GetString("u"), json.GetString("p"));

        }

        public class AdminLteUser : ClaimsIdentity
        {
            public override string Name => "AdminUserInfo";

            public override string AuthenticationType => "AdminLTE";
        }
    }

}
